/***************************************************************************
 *	@file 		fetion_contact.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		source file of fetion groups and contacts, contain operation
 *				function of groups and contacts
 *	@modify		none
 ***************************************************************************/

#include "fetion_util.h"
#include "fetion_exception.h"
#include "fetion_contact.h"
#include "fetion_user.h"
#include "fetion_database.h"
#include "fetion_config.h"
#include "fetion_sip.h"

int Group::load_from_database(SQLiteStatement *stmt)
{
	int ret = FUNC_SUCC;

	if(NULL == stmt) {
		throw fetionException("SQL statement is NULL.");
	}
	groupId = stmt->ValueInt(0);
	groupName = stmt->ValueString(1);
	dirty = 0;

	return ret;
}

bool Group::save_to_database(SQLiteWrapper &sqlite)
{
	std::stringstream SQL;
	SQL<<"insert into groups values ("<<groupId<<",'"<<groupName<<"');";
//	std::cout<<SQL.str()<<std::endl;
	return sqlite.DirectStatement(SQL.str());
}

bool Contact::operator< (Contact &contact)
{
	if(this->groupid != contact.groupid) {
		return this->groupid > contact.groupid;
	}
	else {
		if(P_ONLINE == this->state || contact.state <= 0) {
			return true;
		}
		else if(P_ONLINE == contact.state || this->state <= 0) {
			return false;
		}
		else {
			return (this->state < contact.state);
		}
	}
}

int Contact::load_from_database(SQLiteStatement *stmt)
{
	int ret = FUNC_SUCC;

	if(NULL == stmt) {
		throw fetionException("SQL statement is NULL.");
	}
	
	userId 				= stmt->ValueString(0);
	sId 				= stmt->ValueString(1);
	sipuri 				= stmt->ValueString(2);
	localname 			= stmt->ValueString(3);
	nickname 			= stmt->ValueString(4);
	impression 			= stmt->ValueString(5);
	mobileno 			= stmt->ValueString(6);
	devicetype 			= stmt->ValueString(7);
	portraitCrc 		= stmt->ValueString(8);
	birthday 			= stmt->ValueString(9);
	country 			= stmt->ValueString(10);
	province 			= stmt->ValueString(11);
	city 				= stmt->ValueString(12);
	identity 			= stmt->ValueInt(13);
	scoreLevel 			= stmt->ValueInt(14);
	serviceStatus 		= stmt->ValueInt(15);
	carrierStatus 		= stmt->ValueInt(16);
	relationStatus 		= stmt->ValueInt(17);
	carrier 			= stmt->ValueString(18);
	groupid 			= stmt->ValueInt(19);
	gender 				= stmt->ValueInt(20);
	dirty = 0;

	return ret;
}

bool Contact::save_to_database(SQLiteWrapper &sqlite)
{
	std::stringstream SQL;
	std::string local_name = (localname.find("\'", 0) == std::string::npos)?localname:"";
	std::string nick_name = (nickname.find("\'", 0) == std::string::npos)?nickname:"";
	std::string impres= (impression.find("\'", 0) == std::string::npos)?impression:"";
	
	SQL<<"insert into contacts values ("
	   <<"'"<<userId<<"','"<<sId<<"','"<<sipuri<<"','"
	   <<local_name<<"','"<<nick_name<<"','"<<impres<<"','"
	   <<mobileno<<"','"<<devicetype<<"','"<<portraitCrc<<"','"
	   <<birthday<<"','"<<country<<"','"<<province<<"','"<<city<<"','"
	   <<identity<<"',"<<scoreLevel<<","<<serviceStatus<<","<<carrierStatus<<","
	   <<relationStatus<<",'"<<carrier<<"',"<<groupid<<","<<gender<<");";
	return sqlite.DirectStatement(SQL.str());
}

int Contact::parse_contact_from_presence_p(xmlpp::Element *elem)
{
	int ret = FUNC_SUCC;
	std::string tmp;
	
	tmp.clear();
	if(NULL != elem) {
		tmp = elem->get_attribute_value("sid");
		if(!tmp.empty()) {
			sId = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("m");
		if(!tmp.empty()) {
			mobileno = tmp;	tmp.clear();
		}
		tmp = elem->get_attribute_value("l");
		if(!tmp.empty()) {
			scoreLevel = atoi(tmp.c_str());
			tmp.clear();
		}
		tmp = elem->get_attribute_value("n");
		if(!tmp.empty()) {
			nickname = tmp;	tmp.clear();
		}
		tmp = elem->get_attribute_value("i");
		if(!tmp.empty()) {
			impression = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("p");
		if(!tmp.empty()) {
			if(portraitCrc == tmp || tmp == "0") {
				imageChanged = 0;
			}
			else {
				imageChanged = 1;
			}
			portraitCrc = tmp;
			tmp.clear();
		}
		else {
			imageChanged = 0;
		}
		tmp = elem->get_attribute_value("c");
		if(!tmp.empty()) {
			carrier = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("cs");
		if(!tmp.empty()) {
			carrierStatus = atoi(tmp.c_str());
			tmp.clear();
		}
		tmp = elem->get_attribute_value("s");
		if(!tmp.empty()) {
			serviceStatus = atoi(tmp.c_str());
			tmp.clear();
		}
	}
	return ret;
}

int Contact::parse_contact_from_presence_pr(xmlpp::Element *elem)
{
	int ret = FUNC_SUCC;
	std::string tmp;

	if(NULL != elem) {
		tmp = elem->get_attribute_value("dt");
		if(!tmp.empty()) {
			devicetype = tmp; tmp.clear();
		}
		else {
			devicetype = "PC";
		}
		tmp = elem->get_attribute_value("b");
		if(!tmp.empty()) {
			state = atoi(tmp.c_str());
			tmp.clear();
		}
	}
	return ret;
}

int Contact::parse_from_sipc(xmlpp::Element *elem)
{
	int ret = FUNC_SUCC;
	std::string tmp;

	try {
		if(NULL == elem) {
			throw fetionException("xml element is NULL.");
		}
		tmp.clear();
		tmp = elem->get_attribute_value("uri");
		if(!tmp.empty()) {
			sipuri = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("user-id");
		if(!tmp.empty()) {
			userId = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("local-name");
		if(!tmp.empty()) {
			localname = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("buddy-lists");
		if(!tmp.empty()) {
			groupid = atoi(tmp.c_str()); tmp.clear();
		}
		tmp = elem->get_attribute_value("relation-status");
		if(!tmp.empty()) {
			relationStatus = atoi(tmp.c_str()); tmp.clear();
		}
		else {
			relationStatus = 1;
		}		
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse contact from sipc failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int Contact::parse_from_get_info_response(xmlpp::Element *elem)
{
	int ret = FUNC_SUCC;
	std::string tmp;
	std::string::size_type pos;
	
	try {
		if(NULL == elem) {
			throw fetionException("xml element is NULL.");
		}
		tmp = elem->get_attribute_value("uri");
		if(!tmp.empty()) {
			sipuri = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("user-id");
		if(!tmp.empty()) {
			userId = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("sid");
		if(!tmp.empty()) {
			sId = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("mobile-no");
		if(!tmp.empty()) {
			mobileno = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("carrier");
		if(!tmp.empty()) {
			carrier = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("carrier-status");
		if(!tmp.empty()) {
			carrierStatus = atoi(tmp.c_str()); tmp.clear();
		}
		tmp = elem->get_attribute_value("nickname");
		if(!tmp.empty()) {
			nickname = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("gender");
		if(!tmp.empty()) {
			gender = atoi(tmp.c_str()); tmp.clear();
		}
		tmp = elem->get_attribute_value("birth-date");
		if(!tmp.empty()) {
			birthday = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("impresa");
		if(!tmp.empty()) {
			impression = tmp; tmp.clear();
		}
		tmp = elem->get_attribute_value("carrier-region");
		if(!tmp.empty()) {
			pos = tmp.find(".", 0);
			country = tmp.substr(0, pos);
			province = tmp.substr(pos+1, tmp.length()-pos-1);
			pos = province.find(".", 0);
			city = province.substr(pos+1, province.length()-pos-1);
			province = province.substr(0, pos);
			pos = city.find(".", 0);
			city = city.substr(0, pos);
		}
		groupid = BUDDY_LIST_NOT_GROUPED;
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "parse contact from get information response failed : %s.", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

int Contact::generate_get_info_body(std::string &body, const std::string &no)
{
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *node;
	xmlpp::Element *elem;
	std::string uri, res;
	int ret = FUNC_SUCC;

	try {
		body = "<args></args>";
		if(no.length() == MOBILENO_LEN) {
			uri = "tel:" + no;
		} else if(no.length() == FETIONNO_LEN){
			uri = "sip:" + no;
		} else {
			throw fetionException("number is neither mobile number nor fetion number.");
		}
		parser.parse_memory(body);
		doc = parser.get_document();
		node = doc->get_root_node();
		elem = node->add_child("contact");
		elem->set_attribute("uri", uri);
		body = doc->write_to_string();
		xml_convert(body);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_WARNING, "generate get contact's information body failed : %s", ex.what());
		ret = FUNC_FAIL;
	}
	return ret;
}

void Contact::print_information()
{
	std::string status;
	std::string gd;
	std::string region;
	
	switch(carrierStatus) {
		case STATUS_NORMAL:
			status = "normal status"; break;
		case STATUS_OFFLINE:
			status = "offline , deleted you from his list or out of service"; break;
		case STATUS_NOT_AUTHENTICATED:
			status = "has not accept your add buddy request"; break;
		case STATUS_SMS_ONLINE:
			status = "has not start fetion service"; break;
		case STATUS_REJECTED:
			status = "rejected your add buddy request"; break;
		case STATUS_SERVICE_CLOSED:
			status = "has closed fetion service"; break;
		case STATUS_NOT_BOUND:
			status = "not bound fetion number to a mobile number"; break;
		default:
			status = "unknown status"; break;
	}
	switch(gender) {
		case 0:
			gd = "private"; break;
		case 1:
			gd = "male"; break;
		case 2:
			gd = "female"; break;
		default:
			gd = "spring brother"; break;
	}

	region = country+"-"+province+"-"+city;
	sysinfoString sstr("fetion ID: %s, user ID: %s, mobile number: %s\n"
		"carrier: %s, carrier status: %s, nick name: %s\n"
		"gender: %s, birthday: %s, carrier region: %s\n"
		"impression: %s\n> ", sId.empty()?"none":sId.c_str(), userId.empty()?"none":userId.c_str(), 
		mobileno.empty()?"none":mobileno.c_str(), carrier.empty()?"none":carrier.c_str(), 
		status.c_str(), nickname.empty()?"none":nickname.c_str(), gd.c_str(), birthday.empty()?"none":birthday.c_str(),
		region.c_str(), impression.c_str());
	stdout<<sstr;
}

void Contact::clear()
{
	userId.clear();	sId.clear();					
	sipuri.clear(); nickname.clear();				
	impression.clear();	mobileno.clear();				
	country.clear(); province.clear();				
	city.clear(); state = 0; gender = 0;	

	index = 0; localname.clear(); 
	devicetype.clear();	portraitCrc.clear();			
	birthday.clear(); identity = 0;						
	scoreLevel = 0;	serviceStatus = 0;					
	carrierStatus = 0; relationStatus = 0;
	carrier.clear(); groupid = 0;						
	imageChanged = 0; dirty = 0;                          
}

bool Contact::equal_by_contact_string::operator() (const Contact &contact)
{
//	unsigned int i;

	if(contact.mobileno == _str || contact.sId == _str || contact.userId == _str
			|| contact.nickname == _str || contact.localname == _str) {
		return true;
	}
/*	i = atoi(_str.c_str());
	std::cout<<i<<std::endl;
	if(i == contact.index) {
		return true;
	}*/
	return false;
}

void BuddyApply::prompt()
{
	std::string phrase;
	currentUser *user = currentUser::get_instance();

	std::vector<SingleApply>::iterator iter;
	listenString lstr("\nreceive add buddy requests:\n"
		"--------------------------------------------\n"
		"id  content\n");
	stdout<<lstr;
	for(iter = app_.begin(); iter != app_.end(); ++iter) {		
		user->config->get_phrase(iter->phraseId, phrase);
		lstr("%-3d Hello, I am %s, %s\n",
			iter->applyId, iter->desc.c_str(), phrase.c_str());
		stdout<<lstr;
	}
	lstr("--------------------------------------------\n");
	sysinfoString sstr(
		"type \"accept\" + applicaion id to accept the request.\n"
		"type \"refuse\" + applicaion id to refuse the request.\n>");
	stdout<<lstr<<sstr;
}

std::string BuddyApply::generate_handle_body(const int index, const bool result/*, Contact &contact*/)
{
	xmlParser parser;
	xmlpp::Document *doc;
	xmlpp::Node *pNode;
	xmlpp::Element *elem = NULL;
	std::string body = "<args></args>";
	SingleApply apply = app_[index];
	
	try {
		parser.parse_memory(body);
		doc = parser.get_document();
		pNode = doc->get_root_node();
		elem = pNode->add_child("contacts");
		pNode = dynamic_cast<xmlpp::Node*>(elem);
		elem = pNode->add_child("buddies");
		pNode = dynamic_cast<xmlpp::Node*>(elem);
		elem = pNode->add_child("buddy");
		if(NULL == elem) {
			throw fetionException("can't add attribute to node buddy.");
		}
		elem->set_attribute("user-id", apply.userid);
//		contact.userId = apply.userid;
		elem->set_attribute("uri", apply.sipuri);
//		contact.sipuri = apply.sipuri;
//		contact.relationStatus = 1;
		if(result) {
			elem->set_attribute("result", "1");
		}
		else {
			elem->set_attribute("result", "0");
		}
		elem->set_attribute("buddy-lists", "0");
		elem->set_attribute("expose-mobile-no", "1");
		elem->set_attribute("expose-name", "1");
		elem->set_attribute("local-name", "");
		body = doc->write_to_string();
		xml_convert(body);
	}
	catch (std::exception &ex) {
		WRITE_LOG(LOG_FATAL, "generate add buddy sip body failed : %s.", ex.what());
		return std::string();
	}
	return body;	
}

bool BuddyApply::remove_apply(const std::string &user_id, std::string &name)
{
	for(std::vector<SingleApply>::iterator iter = app_.begin();
			iter != app_.end(); ++iter) {
		if(user_id == iter->userid) {
			name = iter->desc;
			app_.erase(iter);
			return true;
		}
	}
	return false;
};

